#include <ani.h>
#include <array>
#include <iostream>
#include <vector>

std::string ANIUtils_ANIStringToStdString(ani_env *env,ani_string ani_str){
    ani_size  strSize;
    env->String_GetUTF8Size(ani_str, &strSize);
   
    std::vector<char> buffer(strSize + 1); // +1 for null terminator
    char* utf8_buffer = buffer.data();

    //String_GetUTF8 Supportted by https://gitee.com/openharmony/arkcompiler_runtime_core/pulls/3416
    ani_size bytes_written = 0;
    env->String_GetUTF8(ani_str, utf8_buffer, strSize + 1, &bytes_written);
    
    utf8_buffer[bytes_written] = '\0'; 
    std::string content = std::string(utf8_buffer); 
    return content;
}

static void callWithRecord([[maybe_unused]] ani_env *env, [[maybe_unused]] ani_object object, ani_object record)
{
    ani_class recordCls;
    const char * recordClassName = "escompat.Record";
    if (ANI_OK != env->FindClass(recordClassName, &recordCls)) {
        std::cerr << "Not found '" << recordClassName << "'" << std::endl;
        return ;
    }
 
    ani_string ani_name;
    std::string name = "Chloe";
    if (ANI_OK !=env->String_NewUTF8(name.c_str(), name.length(), &ani_name)){
        std::cerr << "String_NewUTF8 Failed '" << "Chloe" << "'" << std::endl;
        return ;
    }

    ani_ref person;
    if (ANI_OK != env->Object_CallMethodByName_Ref(record, "$_get", nullptr, &person, ani_name)){
        std::cerr << "Object_CallMethodByName_Ref  $_get Faild" << std::endl;
        return ;
    }

    ani_ref person_name;
    if (ANI_OK != env->Object_GetFieldByName_Ref(static_cast<ani_object>(person), "name", &person_name)){
        std::cerr << "Object_GetFieldByName_Ref Faild" << std::endl;
        return ;
    }

    ani_double person_age;
    if (ANI_OK != env->Object_GetFieldByName_Double(static_cast<ani_object>(person), "age", &person_age)){
        std::cerr << "Object_GetFieldByName_Ref Faild" << std::endl;
        return ;
    }

    auto person_name_str = ANIUtils_ANIStringToStdString(env, static_cast<ani_string>(person_name));

    std::cout << "Name :" << person_name_str.c_str() << " Age: " << person_age <<  std::endl;
    return;
}

ANI_EXPORT ani_status ANI_Constructor(ani_vm *vm, uint32_t *result)
{
    ani_env *env;
    if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
        std::cerr << "Unsupported ANI_VERSION_1" << std::endl;
        return ANI_ERROR;
    }

    static const char *className = "ani_record.Foo";
    ani_class cls;
    if (ANI_OK != env->FindClass(className, &cls)) {
        std::cerr << "Not found '" << className << "'" << std::endl;
        return ANI_ERROR;
    }

    std::array methods = {
        ani_native_function {"callWithRecord", "C{escompat.Record}:", reinterpret_cast<void *>(callWithRecord)},
    };
    std::cout << "Start bind native methods to '" << className << "'" << std::endl;

    if (ANI_OK != env->Class_BindNativeMethods(cls, methods.data(), methods.size())) {
        std::cerr << "Cannot bind native methods to '" << className << "'" << std::endl;
        return ANI_ERROR;
    };
    std::cout << "Finish bind native methods to '" << className << "'" << std::endl;

    *result = ANI_VERSION_1;
    return ANI_OK;
}